/**
* AutoSWF - Cody Cushing. March 26, 2009
* Copyright (c) 2009 Cody Cushing
* No use restrictions. Maintain this notice in all versions. 
**/

package com.autoswf.locator
{
	import com.autoswf.core.Link;
	import com.autoswf.core.Transport;
	
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.MouseEvent;
	
	import flexunit.framework.TestCase;

	public class LocatorTest extends TestCase
	{
		private var locator:Locator;
		private var mockResource1:IMockResource1;
		private var mockResource2:IMockResource2;
		
		public function LocatorTest(methodName:String=null)
		{
			super(methodName);
		}
		
		override public function setUp():void
		{
			locator = new Locator("locatorId");
			mockResource1 = new MockResource1(); /* implements IMockResource1 */
			mockResource2 = new MockResource2(); /* implements IMockResource1, IMockResource2 */
		}
		
		/**
		 * Tests that registered resources are properly indicated. 
		 */		
		public function testHasService():void
		{
			locator.addResource(mockResource1, MockResource1);
			locator.removeResource(MockResource1);
			assertFalse("resource still present after removal", locator.hasResource(MockResource1));
		}

		public function testAddConcreteService():void
		{
			locator.addResource(mockResource1, MockResource1);
			assertTrue("failed to indicate resource", locator.hasResource(MockResource1));			
		}
		
		/**
		 * Tests that a concrete class can be exposed as an interface. 
		 */		
		public function testAddInterfaceService():void
		{
			locator.addResource(new MockResource1(), IMockResource1);
			assertFalse("Registered to interface but availble by concrete", locator.hasResource(MockResource1));
		}
		
		/**
		 * Tests that related class definition keys dont point to wrong resource.  
		 */		
		public function testPluralRegistration():void
		{
			locator.addResource(mockResource2, MockResource2);
			assertFalse("indicated unregistered resource", locator.hasResource(IMockResource2));
			
			locator.addResource(mockResource2, IMockResource1);
			assertTrue("failed to indicate extended interface resource", locator.hasResource(IMockResource1));			
		}
		
		/**
		 * Tests that locator throws an error if a resource is registered by a definition it does not support.
		 */		
		public function testInvalidRegistration():void
		{
			var flag:Boolean = false;
			try
			{
				locator.addResource(mockResource1, IMockResource2);
			}
			catch(error:Error)
			{
				flag = true;
				assertFalse("Service still registered to invalid definition", locator.hasResource(IMockResource1))
			}
			assertTrue("Registering resource to unsupported class definition did not throw error", flag);
		}
		
		/**
		 * Tests that an error is thrown when overwriting an existing resource. 
		 */		
		public function testRegistrationCollision():void
		{
			locator.addResource(mockResource1, IMockResource1);
			
			var flag:Boolean = false;
			try
			{
				locator.addResource(mockResource2, IMockResource1);
			}
			catch(error:IllegalOperationError)
			{
				flag = true;
			}
			assertTrue("Key collision did not throw error", flag);
		}
		
		/**
		 * Tests that a resource can be registered from somewhere down the chain.
		 */		
		public function testAddServiceRemotely():void
		{
			var link:Link = new Link("childLink");
			link.addParentLink(locator);
			locator.addChildLink(link);
			
			var resourceRegistration:ResourceRegistration = new ResourceRegistration(mockResource1, IMockResource1)
			link.sendUp(resourceRegistration);
			
			var resourceRequest:ResourceRequest = new ResourceRequest(IMockResource1);
			link.sendUp(new Transport(resourceRequest));
			
			assertStrictlyEquals("resource reference not set in request object", mockResource1, resourceRequest.result);
		}
		
		/**
		 * Tests that a reference to the resource is set in the ServiceRequest when that resource exists. 
		 */		
		public function testSyncronousRequest():void
		{
			locator.addResource(mockResource1, IMockResource1);
			
			var resourceRequest:ResourceRequest = new ResourceRequest(IMockResource1);
			locator.sendUp(new Transport(resourceRequest));
			
			assertStrictlyEquals("resource reference not set in request object", mockResource1, resourceRequest.result);
		}
		
		/** 
		 * Tests that the handler is invoked when the resource is located.
		 */		
		public function testAsyncronousRequest():void
		{
			locator.addResource(mockResource1, IMockResource1);
									
			var handlerInvoked:Boolean = false;
			var handler:Function = function(payload:IMockResource1):void
			{
				assertStrictlyEquals("failed to return remotely added instance", mockResource1, payload);
				handlerInvoked = true;
			}
			var resourceRequest:ResourceRequest = new ResourceRequest(IMockResource1, handler);
			locator.sendUp(new Transport(resourceRequest));
			
			assertTrue("handler not invoked on asyncronous resource request", handlerInvoked);
		}

		/**
		 * Tests that locator provides resource to handler. 
		 */		
		public function testDirectServiceAccess():void
		{
			locator.addResource(mockResource1, IMockResource1);
									
			var resourceRequest:ResourceRequest = new ResourceRequest(IMockResource1);
			locator.sendUp(new Transport(resourceRequest)); /* should sendUpChain wrap requests in Transport?*/
			
			assertStrictlyEquals("resource reference not set when directly requesting resource", mockResource1, resourceRequest.result);
		}
		
		/**
		 * Tests that locator provides resource to handler when child requests it.
		 */		
		public function testChildServiceAccess():void
		{
			locator.addResource(mockResource1, IMockResource1);
									
			var link:Link = new Link("desperatelySeekingFoo");
			link.addParentLink(locator);
			locator.addChildLink(link);

			var resourceRequest:ResourceRequest = new ResourceRequest(IMockResource1);
			link.sendUp(resourceRequest);
			
			assertStrictlyEquals("resource reference not set in child request object", mockResource1, resourceRequest.result);
		}
		
		/**
		 * Tests that locator passes resource request up the chain if it does not have the resource. 
		 */		
		public function testServiceLocatorTierBubbling():void
		{
			var parentLocator:Locator = new Locator("parentLocator");
			parentLocator.addResource(mockResource1, IMockResource1);
			
			locator.addParentLink(parentLocator);
			parentLocator.addChildLink(locator);
									
			var link:Link = new Link("client");
			link.addParentLink(locator);
			locator.addChildLink(link);

			var resourceRequest:ResourceRequest = new ResourceRequest(IMockResource1);
			link.sendUp(resourceRequest);
			
			assertStrictlyEquals("resource reference not set in request object", mockResource1, resourceRequest.result);
		}
		
		
		/**
		 * Tests that a resource can be synchronously retrieved in one line using "from" method.
		 */		
		public function testFrom():void
		{
			var link:Link = new Link("childLink");
			link.addParentLink(locator);
			locator.addChildLink(link);
			locator.addResource(mockResource1, IMockResource1);
			
			assertStrictlyEquals("visit did not return the same instance", 
			    mockResource1, new ResourceRequest(IMockResource1).from(link) );
		}
		
	}
}